package com.forester.foresterplatform.common.generator;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用于生成 实体类、Controller、Service、通用Mapper。
 */
@Service
public final class CodeGenerator {
    @Autowired
    public JdbcTemplate jdbcTemplate;

    /**
     * 下划线转驼峰
     * @param str
     * @return
     */
    private String underLineToCamel(String str) {
        StringBuilder result = new StringBuilder();
        String a[] = str.split("_");
        for (String s : a) {
            if(result.length() == 0)
                result.append(StringUtils.uncapitalize(s));
            else
                result.append(StringUtils.capitalize(s));
        }
        return result.toString();
    }

    /**
     * 在 entityPath 路径下依据数据库中的 tableName 生成 className.java 文件
     * @param entityPath
     * @param basePackage
     * @param tableName
     * @param className
     */
    public void generateEntity(String entityPath, String basePackage, String tableName, String className) {
        // 获取元数据信息
        String sql = "select column_name, column_type, column_comment from information_schema.COLUMNS where TABLE_NAME = '" + tableName + "' and TABLE_SCHEMA = (select database());";

        // 获得去除公有字段后的GeneratorField列表
        Set<String> commonField = Set.of("id", "createdTime", "createdBy", "updatedTime", "updatedBy", "isDeleted", "remark");
        List<GeneratorField> fields = jdbcTemplate.queryForList(sql)
                .stream()
                .map(this::buildFieldFromMap)
                // 过滤公有字段
                .filter(field -> !commonField.contains(field.getName()))
                .collect(Collectors.toList());

        // 通过 FreeMarker 生成实体类
        Map<String, Object> params = Map.of(
                "fields", fields,
                "className", className,
                "tableName", tableName,
                "basePackage", basePackage);
        generateClass(entityPath, "entity.java.ftl", className, params);
    }

    /**
     * 在 servicePath 生成 className.java 文件
     * @param servicePath
     * @param basePackage
     * @param className
     */
    private void generateService(String servicePath, String basePackage, String className) {
        // 通过freeMarker生成类
        Map<String, Object> params = Map.of(
                "className", className,
                "basePackage", basePackage);
        generateClass(servicePath, "service.java.ftl", className + "Service", params);
    }

    /**
     * 在 mapperPath 生成 className.java 文件
     * @param mapperPath
     * @param basePackage
     * @param className
     */
    private void generateMapper(String mapperPath, String basePackage, String className) {
        // 通过freeMarker生成类
        Map<String, Object> params = Map.of(
                "className", className,
                "basePackage", basePackage);

        generateClass(mapperPath, "mapper.java.ftl", className + "Mapper", params);
    }

    /**
     * 在 serviceImplPath 下生成 className.java 文件
     * @param serviceImplPath
     * @param basePackage
     * @param className
     */
    private void generateServiceImpl(String serviceImplPath, String basePackage, String className) {
        // 通过freeMarker生成类
        Map<String, Object> params = Map.of(
                "className", className,
                "basePackage", basePackage);

        generateClass(serviceImplPath, "serviceImpl.java.ftl", className + "ServiceImpl", params);
    }

    /**
     * 在 controllerPath 下生成 className.java 文件
     * @param controllerPath
     * @param basePackage
     * @param className
     */
    public void generateController(String controllerPath, String basePackage, String className) {
        // 通过freeMarker生成类
        Map<String, Object> params = Map.of(
                "className", className,
                "basePackage", basePackage);

        generateClass(controllerPath, "controller.java.ftl", className + "Controller", params);
    }

    /**
     * Map 类型转换为 GeneratorField 类型
     * @return
     */
    private GeneratorField buildFieldFromMap(Map<String, Object> map) {
        String columnName = map.get("column_name").toString();
        String columnType = map.get("column_type").toString();
        String columnComment = map.get("column_comment").toString();

        // 根据数据库类型字符串 获得 CodeGeneratorType
        CodeGeneratorType type = CodeGeneratorType.of(columnType).orElse(CodeGeneratorType.VARCHAR);

        // 字段名转换为驼峰
        columnName = underLineToCamel(columnName);

        // 封装
        return new GeneratorField(columnName, type, columnComment);
    }

    /**
     * 依据 templateName 模板, 使用 params 参数在 path 路径下生成 className.java 类
     * @param path
     * @param templateName
     * @param params
     */
    private void generateClass(String path, String templateName, String className, Map<String, Object> params) {

        // 目录不存在的时候创建目录
        File file = Path.of(path).toFile();
        if(!file.exists())
            file.mkdirs();

        Configuration configuration = new Configuration(Configuration.VERSION_2_3_32);
        configuration.setDefaultEncoding("UTF-8");
        configuration.setClassLoaderForTemplateLoading(CodeGenerator.class.getClassLoader(), "templates");

        try {
            Template template = configuration.getTemplate(templateName);
            template.process(params, Files.newBufferedWriter(Path.of(path + className + ".java")));
        } catch (IOException | TemplateException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成 Pojo、Mapper、Service/ServiceImpl、Controller
     * @param basePath
     * @param tableName
     * @param className
     */
    public void generate(String basePath, String tableName, String className) {
        String entityPath = "src/main/java/" + basePath + "/entity/";
        String basePackage = pathToPackage(basePath);
        generateEntity(entityPath, basePackage, tableName, className);

        String daoPath = "src/main/java/" + basePath + "/mapper/";
        generateMapper(daoPath, basePackage, className);

        String servicePath = "src/main/java/" + basePath + "/service/";
        generateService(servicePath, basePackage, className);

        String serviceImplPath = "src/main/java/" + basePath + "/service/impl/";
        generateServiceImpl(serviceImplPath, basePackage, className);

        String controllerPath = "src/main/java/" + basePath + "/controller/";
        generateController(controllerPath, basePackage, className);

    }

    /**
     * 将path转换为包名
     */
    private String pathToPackage(String path) {
        String pkg = path.replace("/", ".").replace("\\", ".");
        return pkg.endsWith(".") ? pkg.substring(0, pkg.length() - 1) : pkg;
    }
}
